
import os
import wx

import wx.xrc as xrc
import wx.lib.customtreectrl as ct

import xml.dom.minidom

from wx.lib.expando import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED

try:
    from agw import pycollapsiblepane as PCP
except ImportError:  # if it's not there locally, try the wxPython lib.
    import wx.lib.agw.pycollapsiblepane as PCP

import ab_plugins as ap
import ab_exceptions
import ab_ids as ai
import ab_utils as au


class GeneratorPromptDialog(wx.Dialog):
    def __init__(self, parent, app, title, prompt,
                 showok=True, init_values=None):
        self.app = app
        self.component = dict()

        wx.Dialog.__init__(self, None, -1, title,
                           style=wx.DEFAULT_FRAME_STYLE | wx.STAY_ON_TOP)
        v_sizer = wx.BoxSizer(wx.VERTICAL)
        panel_ctrl = wx.Panel(self, style=wx.EXPAND | wx.ALL)

        # if xrc is XML, use xmlResource instead
        prompt = prompt.strip()
        if prompt.startswith('<?xml'):
            ret = self.createWithXrc(panel_ctrl, prompt, init_values or dict())
        else:
            ret = self.createWithPrompts(panel_ctrl, prompt,
                                         init_values or dict())

        if ret:
            v_sizer.Add(panel_ctrl, 0, wx.ALL | wx.EXPAND, 5)

        if showok:
            v_sizer.Add(wx.StaticLine(self, -1, size=(20, -1),
                        style=wx.LI_HORIZONTAL),
                        0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
            v_sizer.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL),
                        0, wx.ALIGN_RIGHT, 5)

        self.SetSizer(v_sizer)
        self.Fit()

    def createWithXrc(self, panel_ctrl, xrc_str, init_values):
        panel = None
        resource = None
        # supported control list
        ctrl_list = ('wxTextCtrl', 'wxComboBox', 'wxRadioBox',
                     'wxSpinCtrl', 'wxListCtrl')

        dom = xml.dom.minidom.parseString(xrc_str)
        for rs in dom.getElementsByTagName('resource'):
            for s in rs.getElementsByTagName('object'):
                name, class_name = ('', '')

                if s.hasAttribute('name'):
                    name = s.getAttribute('name')
                if s.hasAttribute('class'):
                    class_name = s.getAttribute('class')
                # ONLY handle the first panel
                if class_name == 'wxPanel' and resource is None:
                    resource = xrc.EmptyXmlResource()
                    resource.LoadFromString(xrc_str)
                    panel = resource.LoadPanel(panel_ctrl, name)

                if (class_name in ctrl_list) and name and resource:
                    ctrl = xrc.XRCCTRL(panel, name)
                    ctrl_name = ':'.join([class_name, name])
                    if ctrl_name in self.component:
                        self.app.warn('"%s" is existent on xrc' % name)

                    self.component[ctrl_name] = ctrl
                    val = init_values.get(name)
                    if val:
                        ctrl.SetValue(val)

            if resource:
                return panel

        return panel

    def createWithPrompts(self, panel_ctrl, prompt, init_values):
        ret = True
        g_sizer = wx.FlexGridSizer(0, 2, 0, 0)
        g_sizer.AddGrowableCol(1)

        k = 0
        listp = prompt.split('\n')
        for k in xrange(len(listp)):
            f = listp[k].strip().split()

            if len(f) < 2:
                continue

            tp, name = f[:2]
            title = self.convertTitleInPrompt(name)

            if tp == 'text':
                # text-line = text value
                g_sizer.Add((0, 0))
                init_values = ' '.join(f[1:])
                g_sizer.Add(wx.StaticText(panel_ctrl, -1, init_values))
            elif tp == 'choice':
                # choice-line=choice name value,value,...
                init_values = ' '.join(f[2:]).split(',')

                radiobox_ctrl = wx.RadioBox(panel_ctrl, -1, title,
                                            wx.DefaultPosition, wx.DefaultSize,
                                            init_values, 4,
                                            wx.RA_SPECIFY_COLS | wx.NO_BORDER)
                g_sizer.Add((0, 0))
                g_sizer.Add(radiobox_ctrl, 1, wx.EXPAND | wx.ALL)

                ctrl_name = 'wxRadioBox:%s' % name
                self.component[ctrl_name] = radiobox_ctrl
            elif tp == 'nchoice':
                # nchoice-line=nchoice name value,value,...
                init_values = ' '.join(f[2:]).split(',')
                choice_ctrl = wx.Choice(panel_ctrl, -1, init_values)

                g_sizer.Add(wx.StaticText(panel_ctrl, -1, title))
                g_sizer.Add(choice_ctrl, 1, wx.EXPAND | wx.ALL)

                ctrl_name = 'wxChoice:%s' % name
                self.component[ctrl_name] = choice_ctrl
            elif tp in ('input', 'number'):
                # input-line=input name [value]
                # number-line=number name [value]
                init_values = ' '.join(f[2:]).strip()

                text_ctrl = wx.TextCtrl(panel_ctrl, -1, init_values)

                g_sizer.Add(wx.StaticText(panel_ctrl, -1, title))
                g_sizer.Add(text_ctrl, 1, wx.EXPAND | wx.ALL)

                ctrl_name = 'wxTextCtrl:%s' % name
                self.component[ctrl_name] = text_ctrl
            elif tp == 'range':
                # range-line=range name start,end[,init_value]
                init_values = ' '.join(f[2:]).split(',')
                if 1 < len(init_values) < 4:
                    spin_ctrl = wx.SpinCtrl(panel_ctrl, -1)

                    spin_ctrl.SetRange(int(init_values[0]),
                                       int(init_values[1]))
                    if len(init_values) == 3:
                        spin_ctrl.SetValue(int(init_values[2]))
                    else:
                        spin_ctrl.SetValue(int(init_values[0]))

                    g_sizer.Add(wx.StaticText(panel_ctrl, -1, title))
                    g_sizer.Add(spin_ctrl, 1, wx.EXPAND | wx.ALL)

                    ctrl_name = 'wxSpinCtrl:%s' % name
                    self.component[ctrl_name] = spin_ctrl
                else:
                    ret = False
            elif tp == 'minput':
                # mpinput-line=mpinut name values
                #              ...
                #              %%
                lines = 1
                if f[-1] == '%%':
                    value = ' '.join(f[2:-1])
                else:
                    value = ' '.join(f[2:])
                    for p in range(k + 1, len(listp)):
                        li = listp[p].strip()
                        if li == '%%':
                            break
                        else:
                            lines += 1
                            value += '\n' + listp[p]

                expando_ctrl = ExpandoTextCtrl(panel_ctrl,
                                               size=(300, 20 * lines))
                expando_ctrl.SetValue(value)
                EVT_ETC_LAYOUT_NEEDED(panel_ctrl, expando_ctrl.GetId(),
                                      self.OnRefit)

                g_sizer.Add(wx.StaticText(panel_ctrl, -1, title))
                g_sizer.Add(expando_ctrl, 1, wx.EXPAND | wx.ALL)

                ctrl_name = 'wxTextCtrl:%s' % name
                self.component[ctrl_name] = expando_ctrl

        panel_ctrl.SetSizer(g_sizer)
        panel_ctrl.Fit()

        return ret

    def getValues(self):
        opaque = dict()

        for ctrl_name, ctrl in self.component.items():
            tp, name = ctrl_name.split(':', 1)
            if tp in ('wxRadioBox', 'wxChoice'):
                opaque[name] = ctrl.GetString(ctrl.GetSelection())
            elif tp in ('wxComboBox', 'wxTextCtrl', 'wxSpinCtrl'):
                opaque[name] = ctrl.GetValue()
            else:
                raise ab_exceptions.AofException('AofException',
                                                 'ControlType: "%s" for "%s" '
                                                 'is not supported' %
                                                 (tp, name))

        # Do escape the defined words
        env = self.app.readKey(ai.CfgParaEnvObject)
        for name, value in opaque.items():
            if isinstance(value, (str, unicode)):
                opaque[name] = env.escape(value)

        return opaque

    def convertTitleInPrompt(self, name):
        k = 0
        new_name = ''

        while k + 1 < len(name):
            if name[k] == '_':
                if name[k + 1] == '_':
                    new_name += '_'
                    k += 1
                else:
                    new_name += ' '
            elif name[k] == '$':
                # convert '$$' to '$' and ignore a single '$' as an indicator
                if name[k + 1] == '$':
                    new_name += '$'
                    k += 1
                # implict the single dollar
            else:
                new_name += name[k]

            k += 1
            if k + 1 == len(name):
                new_name += name[k]

        return new_name

    def OnRefit(self, event):
        self.Fit()
        self.Refresh()


class SaveDialog(wx.Dialog):
    def __init__(self, parent, app, title, enabled_list):
        wx.Dialog.__init__(self, parent, -1, title)
        self.parent = parent

        v_sizer = wx.BoxSizer(wx.VERTICAL)

        list_size = 3
        self.radiobox_ctrl = wx.RadioBox(self, -1, 'Items',
                                         wx.DefaultPosition, wx.DefaultSize,
                                         ('Original (hex/binary) data',
                                          'converted binary data',
                                          'Formatted tree list (.atr)'),
                                         1, wx.RA_SPECIFY_COLS)

        if enabled_list and len(enabled_list) > 0:
            selected = False
            for index in xrange(list_size):
                self.radiobox_ctrl.EnableItem(index, index in enabled_list)
                if not selected and index in enabled_list:
                    selected = True
                    self.radiobox_ctrl.SetSelection(index)

        v_sizer.Add(self.radiobox_ctrl, 0, wx.EXPAND | wx.ALL, 5)
        v_sizer.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL),
                    0, wx.ALIGN_CENTER, 5)

        self.SetSizer(v_sizer)
        self.Fit()

    def getValue(self):
        return self.radiobox_ctrl.GetSelection()


class DumpDialog(wx.Dialog):
    def __init__(self, parent, app, size, line_nb):
        wx.Dialog.__init__(self, parent, -1, 'Content Dump')

        self.size = size

        v_sizer = wx.BoxSizer(wx.VERTICAL)
        v_sizer_field = wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Fields'),
                                          wx.VERTICAL)

        g_sizer = wx.FlexGridSizer(0, 4, 0, 0)
        g_sizer.AddGrowableCol(1)
        g_sizer.AddGrowableCol(3)

        self.spin_ctrl_start = wx.SpinCtrl(self, -1, '')
        self.spin_ctrl_start.SetRange(0, self.size)
        self.spin_ctrl_end = wx.SpinCtrl(self, -1, '')
        self.spin_ctrl_end.SetRange(0, self.size)
        self.spin_ctrl_end.SetValue(self.size - 1)
        g_sizer.Add(wx.StaticText(self, -1, 'Start'), 0, wx.EXPAND | wx.ALL, 1)
        g_sizer.Add(self.spin_ctrl_start, 1, wx.EXPAND | wx.ALL, 1)
        g_sizer.Add(wx.StaticText(self, -1, 'End'), 0, wx.EXPAND | wx.ALL, 1)
        g_sizer.Add(self.spin_ctrl_end, 1, wx.EXPAND | wx.ALL, 1)

        v_sizer_field.Add(g_sizer, 1, wx.EXPAND | wx.ALL)
        v_sizer.Add(v_sizer_field, 0, wx.EXPAND | wx.ALL, 3)

        v_sizer_option = wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Options'),
                                           wx.VERTICAL)
        self.checkbox_ctrl_asc = wx.CheckBox(self, -1,
                                             'Generate the ASCII characters '
                                             'together')
        self.checkbox_ctrl_align = wx.CheckBox(self, -1,
                                               'Start the position to the '
                                               'alignment')
        v_sizer_option.Add(self.checkbox_ctrl_asc, 0, wx.EXPAND | wx.ALL)
        v_sizer_option.Add(self.checkbox_ctrl_align, 0, wx.EXPAND | wx.ALL)

        v_sizer_line = wx.BoxSizer(wx.HORIZONTAL)
        self.checkbox_ctrl_line = wx.CheckBox(self, -1,
                                              'Set character number each line')
        self.text_ctrl_line = wx.TextCtrl(self, -1, str(line_nb))
        v_sizer_line.Add(self.checkbox_ctrl_line, 0, wx.EXPAND | wx.ALL)
        v_sizer_line.Add(self.text_ctrl_line, 1, wx.EXPAND | wx.ALL)
        v_sizer_option.Add(v_sizer_line, 1, wx.EXPAND | wx.ALL)
        v_sizer.Add(v_sizer_option, 1, wx.EXPAND | wx.ALL, 3)

        v_sizer.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL),
                    0, wx.ALIGN_RIGHT | wx.ALL, 3)

        self.SetSizer(v_sizer)
        self.Fit()

        wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk)
        wx.EVT_SPINCTRL(self, self.spin_ctrl_start.GetId(),
                        self.OnSpinCtrlStart)
        wx.EVT_SPINCTRL(self, self.spin_ctrl_end.GetId(), self.OnSpinCtrlEnd)
        wx.EVT_CHECKBOX(self, self.checkbox_ctrl_line.GetId(),
                        self.updateControls)

        self.updateControls()
        self.spin_ctrl_end.SetValue(self.size - 1)

    def OnSpinCtrlStart(self, event):
        value = self.spin_ctrl_end.GetValue()
        if self.spin_ctrl_start.GetValue() >= value:
            self.spin_ctrl_start.SetValue(max(0, value - 1))

        self.updateControls()

    def OnSpinCtrlEnd(self, event):
        value = self.spin_ctrl_start.GetValue()
        if self.spin_ctrl_end.GetValue() <= value:
            self.spin_ctrl_end.SetValue(min(self.size, value + 1))

        self.updateControls()

    def updateControls(self, event=None):
        self.text_ctrl_line.Enable(self.checkbox_ctrl_line.IsChecked())

        self.spin_ctrl_start.SetRange(0, self.spin_ctrl_end.GetValue())
        self.spin_ctrl_end.SetRange(self.spin_ctrl_start.GetValue(), self.size)

    def getValues(self):
        return (self.spin_ctrl_start.GetValue(),
                self.spin_ctrl_end.GetValue(),
                self.checkbox_ctrl_asc.IsChecked(),
                self.checkbox_ctrl_align.IsChecked(),
                int(self.text_ctrl_line.GetValue()))

    def OnOk(self, event):
        try:
            int(self.text_ctrl_line.GetValue())
            self.EndModal(wx.ID_OK)
        except ValueError:
            wx.MessageBox('Line number accepts digits',
                          'Error', wx.ID_OK, wx.ICON_ERROR)


class NewPositionDialog(wx.Dialog):
    def __init__(self, parent, app, size, curpos):
        wx.Dialog.__init__(self, parent, -1, '(0 - %d)' % (size - 1))

        v_sizer = wx.BoxSizer(wx.VERTICAL)
        h_sizer = wx.BoxSizer(wx.HORIZONTAL)
        h_sizer.Add(wx.StaticText(self, -1, 'Offset'), 0,
                    wx.EXPAND | wx.ALL, 3)
        self.spin_ctrl_pos = wx.SpinCtrl(self, -1, '')
        h_sizer.Add(self.spin_ctrl_pos, 1, wx.EXPAND | wx.ALL, 3)

        v_sizer.Add(h_sizer, 0, wx.EXPAND | wx.ALL, 3)
        v_sizer.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL),
                    0, wx.ALIGN_RIGHT | wx.ALL, 3)

        self.spin_ctrl_pos.SetRange(0, size - 1)
        self.spin_ctrl_pos.SetValue(curpos)

        self.SetSizer(v_sizer)
        self.Fit()

    def getValue(self):
        return self.spin_ctrl_pos.GetValue()


class ChoiceDialog(wx.Dialog):
    def __init__(self, parent, app, title, items):
        wx.Dialog.__init__(self, parent, -1, title)

        v_sizer = wx.BoxSizer(wx.VERTICAL)
        self.radiobox_ctrl_items = wx.RadioBox(self, -1, 'Items',
                                               wx.DefaultPosition,
                                               wx.DefaultSize,
                                               items, 1, wx.RA_SPECIFY_COLS)

        v_sizer.Add(self.radiobox_ctrl_items, 0, wx.EXPAND | wx.ALL, 3)
        v_sizer.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL),
                    0, wx.ALIGN_RIGHT | wx.ALL, 3)

        self.SetSizer(v_sizer)
        self.Fit()

    def getValue(self):
        return self.radiobox_ctrl_items.GetSelection()


# TODO: use wxFileDialog to set the panel instead once it's available
class _PlxChooser(wx.Panel):
    def __init__(self, parent, app, layout=True):

        wx.Panel.__init__(self, parent, -1, style=wx.NO_BORDER)

        self.app = app
        self.parent = parent
        self.layout = layout
        self.opaque, self.options = None, None

        self.plx = self.app.readKey(ai.CfgParaPlxObject)

        sizer = wx.BoxSizer(wx.VERTICAL)

        self.cp = PCP.PyCollapsiblePane(self, label='Plug-in Configuration',
                                        agwStyle=wx.CP_NO_TLW_RESIZE)
        self.cp.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnPaneChanged)

        pane = self.cp.GetPane()
        psizer = wx.BoxSizer(wx.VERTICAL)

        psizer.Add(self.initOrganizer(pane), 0, wx.ALL | wx.EXPAND)

        tree = self.initPlxTree(pane)
        self.tree_enabled = tree is not None
        if tree is not None:
            psizer.Add(tree, 1, wx.ALL | wx.ALL | wx.EXPAND)

        pane.SetSizer(psizer)

        sizer.Add(self.cp, 1, wx.ALL | wx.EXPAND)
        self.SetSizer(sizer)
        self.Layout()

        wx.CallAfter(self.initializeSize)

    def initializeSize(self):
        self.w, self.h = self.parent.GetClientSize()

    def initOrganizer(self, pane):
        sizer = wx.StaticBoxSizer(
            wx.StaticBox(pane, -1, 'Front-end Organizer'), wx.HORIZONTAL)

        self.checkbox_ctrl = wx.CheckBox(pane, -1, 'Use Front-end')
        sizer.Add(self.checkbox_ctrl, 0, wx.ALIGN_CENTER_VERTICAL)

        default_val = ''
        org_list = self.plx.getPlxList(ap.PlxTpOrganizer)
        if len(org_list or ''):
            default_val = org_list[-1]

        self.combobox_ctrl = wx.ComboBox(pane, -1, default_val,
                                         choices=org_list,
                                         style=wx.CB_READONLY | wx.CB_DROPDOWN)
        sizer.Add(self.combobox_ctrl, 1, wx.EXPAND | wx.LEFT | wx.RIGHT)
        self.button_ctrl = wx.Button(pane, -1, '>', size=(30, -1))
        sizer.Add(self.button_ctrl, 0, wx.ALIGN_RIGHT)

        if len(default_val) == 0:
            self.checkbox_ctrl.Enable(False)
            self.combobox_ctrl.Enable(False)
            self.button_ctrl.Enable(False)
        else:
            self.checkbox_ctrl.Bind(wx.EVT_CHECKBOX, self.OnEventCheckbox)
            self.button_ctrl.Bind(wx.EVT_BUTTON, self.OnEventButton)
            self.combobox_ctrl.Bind(wx.EVT_COMBOBOX, self.OnEventCombo)

        self.OnEventCheckbox(None)

        return sizer

    def initPlxTree(self, pane):
        # the generator works in a specified method, not to select here
        BANNED_TYPES = (ap.PlxTpOrganizer, ap.PlxTpGenerator)
        ci = self.plx.getCategory()
        cats = [c for c in ci.getCategories() if c not in BANNED_TYPES]
        cats.sort()

        sum = 0
        for c in cats:
            plxs = self.plx.getPlxList(c)
            sum += len(plxs)

        if sum == 0:
            return None

        sizer = wx.StaticBoxSizer(
            wx.StaticBox(pane, -1, 'Enabled Plug-ins'), wx.HORIZONTAL)

        self.cats = dict()
        self.tree = ct.CustomTreeCtrl(pane)
        self.root = self.tree.AddRoot('Plug-ins', ct_type=1)
        # initialize to set root as checked
        self.tree.SetItem3State(self.root, True)
        self.root.Set3StateValue(wx.CHK_CHECKED)
        for c in cats:
            plxs = self.plx.getPlxList(c)
            if len(plxs) == 0:
                continue

            self.cats[c] = self.tree.AppendItem(
                self.root, ci.getNameWithId(c), ct_type=1)
            self.tree.SetItem3State(self.cats[c], True)
            # initialize to set all categories as undetermined
            self.cats[c].Set3StateValue(wx.CHK_UNDETERMINED)
            for p in plxs:
                item = self.tree.AppendItem(self.cats[c], p, ct_type=1)
                self.tree.SetItem3State(item, True)
                # initialize to set all plug-in as undetermined
                item.Set3StateValue(wx.CHK_UNDETERMINED)

        sizer.Add(self.tree, 1, wx.ALL | wx.EXPAND)
        self.tree.Bind(ct.EVT_TREE_ITEM_CHECKED, self.OnTreectrlChecked)

        return sizer

    def OnTreectrlChecked(self, event):
        item = event.GetItem()

        # refer to ab_preferences_dialog.PlxEnablePage
        def _m(value, table):
            return table[value]

        value = _m(item.Get3StateValue(),
                   [wx.CHK_CHECKED, wx.CHK_CHECKED, wx.CHK_UNCHECKED])
        item.Set3StateValue(value)

        all_enabled = True
        all_value = self.root.Get3StateValue()
        for c, v in self.cats.items():
            cat_enabled = True
            val = v.Get3StateValue()

            if val != wx.CHK_CHECKED:
                val = _m(all_value, [wx.CHK_UNCHECKED,
                         wx.CHK_UNDETERMINED, wx.CHK_UNCHECKED])

            va = _m(val, [wx.CHK_UNCHECKED,
                    wx.CHK_UNDETERMINED, wx.CHK_UNDETERMINED])

            child, cookie = self.tree.GetFirstChild(v)
            while child is not None:
                if child.Get3StateValue() != wx.CHK_CHECKED:
                    self.tree.CheckItem2(child, va)
                if child.Get3StateValue() == wx.CHK_UNCHECKED:
                    cat_enabled = False

                child, cookie = self.tree.GetNextChild(v, cookie)

            if v.Get3StateValue() != wx.CHK_CHECKED:
                if all_value == wx.CHK_CHECKED or cat_enabled:
                    self.tree.CheckItem2(v, wx.CHK_UNDETERMINED)
                else:
                    self.tree.CheckItem2(v, wx.CHK_UNCHECKED)

            if not cat_enabled:
                all_enabled = False

        if item is not self.root \
                and self.root.Get3StateValue() != wx.CHK_CHECKED:
            self.root.Set3StateValue(
                _m(all_enabled, [wx.CHK_UNCHECKED, wx.CHK_UNDETERMINED]))

        self.tree.Refresh()
        event.Skip()

    def OnPaneChanged(self, event):
        if self.layout:
            self.parent.Layout()
        elif self.cp.IsExpanded():
            # FIXME: is there a better solution to fit the dialog?
            _, eh = self.parent.WindowToClientSize(
                self.parent.GetEffectiveMinSize())
            self.parent.SetClientSize((self.w, eh))
        else:
            self.parent.SetClientSize((self.w, self.h))

    def OnEventCheckbox(self, event):
        enable = self.checkbox_ctrl.IsChecked()

        self.combobox_ctrl.Enable(enable)
        if enable:
            self.OnEventCombo(None)
        else:
            self.button_ctrl.Enable(False)

    def OnEventButton(self, event):
        self.opaque = self.app.getConfigure(self.parent,
                                            'Organizer Parameters',
                                            self.options)

    def OnEventCombo(self, event):
        name = self.combobox_ctrl.GetString(self.combobox_ctrl.GetSelection())
        inst = self.plx.getInstance(name)
        self.options = au.getAttrFromMethods(inst, ('get_options',))
        if not self.options:
            elem = self.plx.getElement(name)
            self.options = elem.getOption()

        # enable the button for extension panel
        self.button_ctrl.Enable(self.options is not None)

    def getTreeValue(self):
        ret = dict()
        if not self.tree_enabled:
            # return the empty value to leave the behaviour
            # decided by the executor
            return ret
        elif self.root.Get3StateValue() == wx.CHK_CHECKED:
            # return an empty dict as all plug-ins enabled
            return ret

        for c, v in self.cats.items():
            if v.Get3StateValue() != wx.CHK_UNCHECKED:
                # no value in the dict means all plug-ins in the
                # category are enabled
                continue

            plxs = list()
            child, cookie = self.tree.GetFirstChild(v)
            while child is not None:
                if child.Get3StateValue() == wx.CHK_CHECKED:
                    plxs.append(child.GetText())

                child, cookie = self.tree.GetNextChild(v, cookie)

            ret[c] = plxs

        return ret

    def getValues(self):
        plxs, opaque = None, None

        if self.tree_enabled:
            plxs = self.getTreeValue()

        if self.checkbox_ctrl.IsChecked():
            if plxs is None:
                plxs = dict()

            plxs[ap.PlxTpOrganizer] = self.combobox_ctrl.GetString(
                self.combobox_ctrl.GetSelection())
            opaque = self.opaque

        return plxs, opaque


class OpenWithPlxDialog(wx.Dialog):
    def __init__(self, parent, app, title):
        wx.Dialog.__init__(self, parent, -1, title)

        self.app = app
        self.parent = parent

        self.plx = self.app.readKey(ai.CfgParaPlxObject)

        v_sizer = wx.BoxSizer(wx.VERTICAL)

        h_sizer_file = wx.BoxSizer(wx.HORIZONTAL)
        self.text_ctrl = wx.TextCtrl(self, -1)
        h_sizer_file.Add(self.text_ctrl, 1, wx.EXPAND | wx.ALL, 5)
        self.button_file = wx.Button(self, -1, '...', size=(30, -1))
        h_sizer_file.Add(self.button_file, 0, wx.EXPAND | wx.ALL, 5)
        v_sizer.Add(h_sizer_file, 0, wx.EXPAND | wx.ALL)

        self.chooser = _PlxChooser(self, self.app, False)
        v_sizer.Add(self.chooser, 0, wx.EXPAND | wx.ALL, 5)

        v_sizer.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL),
                    0, wx.ALL | wx.ALIGN_RIGHT, 2)

        wx.EVT_BUTTON(self, self.button_file.GetId(), self.OnEventButtonFile)

        self.SetSizer(v_sizer)
        self.Layout()

        # FIXME: a normal way to set the size?
        self.SetClientSize(
            (350, self.WindowToClientSize(self.GetEffectiveMinSize())[1]))

    def OnEventButtonFile(self, event):
        dialog = wx.FileDialog(self, message="Choose a file",
                               defaultDir=os.getcwd(),
                               wildcard=self.plx.getExtension(),
                               style=wx.OPEN | wx.CHANGE_DIR)

        if dialog.ShowModal() == wx.ID_OK:
            self.text_ctrl.SetValue(dialog.GetPaths()[0])

    def getValue(self):
        plx, options = self.chooser.getValues()
        return self.text_ctrl.GetValue(), plx, options


class OpenWithPlxEditorDialog(wx.Dialog):
    def __init__(self, parent, app, title):
        wx.Dialog.__init__(self, parent, -1, title, size=(500, 400),
                           style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)

        self.app = app
        self.parent = parent

        v_sizer = wx.FlexGridSizer(0, 1, 0, 1)
        v_sizer.AddGrowableCol(0)
        v_sizer.AddGrowableRow(0)

        self.text_ctrl = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE)
        self.text_ctrl.SetFont(self.app.readKey(ai.CfgParaDisplayFont))
        v_sizer.Add(self.text_ctrl, 1, wx.EXPAND | wx.ALL, 5)

        self.chooser = _PlxChooser(self, self.app)
        v_sizer.Add(self.chooser, 0, wx.EXPAND | wx.ALL, 5)

        v_sizer.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL),
                    0, wx.ALL | wx.ALIGN_RIGHT, 2)

        self.SetSizer(v_sizer)
        # self.Fit()

    def getValue(self):
        plx, options = self.chooser.getValues()
        return self.text_ctrl.GetValue(), plx, options
